package com.ezandroid.library.network.download;

import android.app.Application;
import android.content.Context;
import android.os.Process;
import android.util.Log;

import com.ezandroid.library.network.util.NetworkUtil;
import com.ezandroid.library.network.util.SDCardUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class DownFileTask extends AbsDownAsyncTask<DownFileParams, Object, DownFileResult> {

    private static final String TAG = "DownFileTask";

    /**
     * 失败.
     */
    public static final int CODE_FAIL_KEY = -1;

    /**
     * 成功.
     */
    public static final int CODE_SUCCESS_KEY = 0;

    private static int HTTP_STATE_SC_OK = 200;
    private static int HTTP_STATE_SC_PARTIAL_CONTENT = 206;
    private static int HTTP_STATE_SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;

    protected static final int BYTE_SIZE = 8192;

    protected static final int MAX_RE_DOWNLOAD_TIMES = 3;

    protected ArrayList<IDownFileProgressListener> mDownFileProgressListeners = new ArrayList<IDownFileProgressListener>();
    protected IDownFileFinishListener mDownFileFinishListener;
    protected DownFileResult mResult = new DownFileResult(CODE_FAIL_KEY, this, null);

    protected String mUrl;
    protected String mFilePath;
    protected long mFileSize = 0;
    protected long mTempFileSize = 0;
    protected int mReDownloadTimes = 0;

    /**
     * 强制使用Application，避免使用Activity造成内存泄露
     */
    private static Application mApplication;

    public DownFileTask(Application application, String url, String filePath) {
        this(application, url, filePath, Process.THREAD_PRIORITY_BACKGROUND);
    }

    public DownFileTask(Application application, String url, String filePath, int priority) {
        super(priority);
        mApplication = application;
        mUrl = url;
        mFilePath = filePath;
    }

    private String encodeURL(String url, String encode) throws UnsupportedEncodingException {
        StringBuilder sb = new StringBuilder();
        StringBuilder noAsciiPart = new StringBuilder();
        for (int i = 0; i < url.length(); i++) {
            char c = url.charAt(i);
            if (c > 255) {
                noAsciiPart.append(c);
            } else {
                if (noAsciiPart.length() != 0) {
                    sb.append(URLEncoder.encode(noAsciiPart.toString(), encode));
                    noAsciiPart.delete(0, noAsciiPart.length());
                }
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 下载文件.
     *
     * @param urlString the url string
     * @param filePath  the file path
     * @return true, if successful
     */
    protected boolean downloadFileFromUrl(String urlString, String filePath) {
        Log.d(TAG, "downloadFileFromUrl : " + urlString + " filePath：" + filePath);
        mReDownloadTimes++;
        boolean flag = false;
        if (mReDownloadTimes >= MAX_RE_DOWNLOAD_TIMES) {
            return flag;
        }
        URL url = null;
        HttpURLConnection con = null;
        try {
            File tempFile = new File(filePath);
            if (tempFile.exists() && tempFile.isFile()) {
                mTempFileSize = tempFile.length();
                Log.d(TAG, "已经下载的文件大小：" + mTempFileSize);
            } else {
                mTempFileSize = 0;
                Log.d(TAG, "未下载过，开始下载");
                File fileParentDir = tempFile.getParentFile();
                if (!fileParentDir.exists()) {
                    fileParentDir.mkdirs();
                }
                tempFile.createNewFile();
            }

            url = new URL(encodeURL(urlString, "utf-8"));
            con = getHttpUrlConnection(url, mApplication);
            // Range头域 用来实现断点续传
            // 表示头500个字节：Range: bytes=0-499
            // 表示第二个500字节：Range: bytes=500-999
            // 表示最后500个字节：Range: bytes=-500
            // 表示500字节以后的范围：Range: bytes=500-
            // 第一个和最后一个字节：Range: bytes=0-0,-1
            // 同时指定几个范围：Range: bytes=500-600,601-999
            con.setRequestProperty("RANGE", "bytes=" + mTempFileSize + "-");
            con.connect();

            InputStream stream = null;
            try {
                int code = con.getResponseCode();
                Log.d(TAG, "code:" + code);
                if (code == HTTP_STATE_SC_OK || code == HTTP_STATE_SC_PARTIAL_CONTENT
                        || code == HTTP_STATE_SC_REQUESTED_RANGE_NOT_SATISFIABLE) {
                    long contentRange = parseFileTotalLengthByContentRange(con);
                    Log.d(TAG, "Content Range:" + contentRange);
                    long contentLength = parseFileTotalLengthByContentLength(con);
                    Log.d(TAG, "Content Length:" + contentLength);
                    mFileSize = Math.max(contentRange, contentLength);
                    Log.d(TAG, "文件总大小:" + mFileSize);
                    if (mTempFileSize > mFileSize) {
                        Log.d(TAG, "临时文件大小比文件总大小还大，删除重新下载");
                        tempFile.delete();
                        // 再次调用downloadFileFromUrl方法
                        flag = downloadFileFromUrl(urlString, filePath);
                    } else if (mTempFileSize == mFileSize && mFileSize != 0) {
                        Log.d(TAG, "临时文件大小与文件总大小相等，不重复下载");
                        publishProgress(mTempFileSize, mFileSize);
                        flag = true;
                    } else {
                        Log.d(TAG, "获取到文件流，开始写入文件");
                        stream = con.getInputStream();
                        flag = writeData2File(stream, filePath);
                    }
                }
            } finally {
                if (stream != null) {
                    try {
                        stream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        flag = false;
                    }
                }
            }
        } catch (IOException e1) {
            e1.printStackTrace();
            flag = false;
        } finally {
            if (con != null) {
                con.disconnect();
            }
        }
        return flag;
    }

    private long parseFileTotalLengthByContentRange(HttpURLConnection con) {
        Map<String, List<String>> headerMap = con.getHeaderFields();
        if (headerMap != null) {
            List<String> contentRange = headerMap.get("Content-Range");
            if (contentRange == null) {
                contentRange = headerMap.get("content-range");
            }
            if (contentRange != null) {
                String rangeString = contentRange.get(0);
                return Long.parseLong(rangeString.substring(rangeString.indexOf('/') + 1));
            }
        }
        return 0L;
    }

    private long parseFileTotalLengthByContentLength(HttpURLConnection con) {
        Map<String, List<String>> headerMap = con.getHeaderFields();
        if (headerMap != null) {
            List<String> contentRange = headerMap.get("Content-Length");
            if (contentRange != null) {
                String rangeString = contentRange.get(0);
                return Long.parseLong(rangeString.substring(rangeString.indexOf('/') + 1));
            }
        }
        return 0L;
    }

    /**
     * 写文件.
     *
     * @param inputStream the input stream
     * @param filePath    the file path
     * @return true, if successful
     */
    protected boolean writeData2File(InputStream inputStream, String filePath) {
        boolean flag = false;
        FileOutputStream fos = null;
        try {
            byte[] data = new byte[BYTE_SIZE];
            Arrays.fill(data, (byte) 0);
            int len = 0;
            boolean sdExist = true;

            fos = new FileOutputStream(filePath, true);
            while ((len = inputStream.read(data, 0, data.length)) != -1) {
                if (!isCancelled()) {
                    if (!SDCardUtil.isStorageWriteable()) {
                        sdExist = false;
                        break;
                    } else {
                        fos.write(data, 0, len);
                        fos.flush();
                        mTempFileSize += len;
                        // 预防临时文件比总文件长度大时的问题
                        if (mFileSize < mTempFileSize) {
                            mFileSize = mTempFileSize;
                        }
                        publishProgress(mTempFileSize, mFileSize);
                    }
                } else {
                    break;
                }
            }

            flag = sdExist;
        } catch (Exception e) {
            flag = false;
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    flag = false;
                }
            }
        }
        return flag;
    }

    public void downloadSync() {
        boolean flag = false;
        while (!isCancelled() && mReDownloadTimes < MAX_RE_DOWNLOAD_TIMES) {
            flag = downloadFileFromUrl(mUrl, mFilePath);
            if (flag) {
                Log.d(TAG, mUrl + " 下载成功");
                break;
            } else {
                Log.d(TAG, mUrl + " 下载失败:" + mReDownloadTimes);
            }
        }
        if (flag) {
            mResult.setCode(CODE_SUCCESS_KEY);
        } else {
            mResult.setCode(CODE_FAIL_KEY);
        }
    }

    @Override
    protected DownFileResult doInBackground(DownFileParams... params) {
        downloadSync();
        return mResult;
    }

    @Override
    protected void onProgressUpdate(Object... values) {
        for (IDownFileProgressListener listener : mDownFileProgressListeners) {
            if (listener != null) {
                listener.onDownFileProgressChanged(this);
            }
        }
    }

    @Override
    protected void onPostExecute(DownFileResult result) {
        super.onPostExecute(result);
        if (mDownFileFinishListener != null) {
            mDownFileFinishListener.onDownFileFinished(result);
        }
    }

    @Override
    protected void onCancelled() {
        // 当任务被取消后，不再回调listener
        mDownFileFinishListener = null;
        super.onCancelled();
    }

    public String getFilePath() {
        return mFilePath;
    }

    public long getTempFileSize() {
        return mTempFileSize;
    }

    public long getFileSize() {
        return mFileSize;
    }

    /**
     * 获取下载进度0~100
     *
     * @return
     */
    public int getProgress() {
        if (mFileSize > 0) {
            return Math.round((mTempFileSize * 100f / mFileSize));
        }
        return 0;
    }

    public IDownFileFinishListener getDownFileFinishListener() {
        return mDownFileFinishListener;
    }

    public void setDownFileFinishListener(IDownFileFinishListener downTaskFinishListener) {
        this.mDownFileFinishListener = downTaskFinishListener;
    }

    public void addDownFileProgressListener(IDownFileProgressListener listener) {
        if (!mDownFileProgressListeners.contains(listener)) {
            mDownFileProgressListeners.add(listener);
        }
    }

    public void removeDownFileProgressListener(IDownFileProgressListener listener) {
        mDownFileProgressListeners.remove(listener);
    }

    private HttpURLConnection getHttpUrlConnection(URL url, Context context)
            throws IOException {
        HttpURLConnection httpConnection;
        if (NetworkUtil.isWapNet(context)) {// wap 网络
            String tempUrl = url.toString();
            int offset = tempUrl.startsWith("https") ? 8 : 7;
            if (offset == 7) {// http开头的
                int contentBeginIdx = tempUrl.indexOf('/', offset);
                StringBuffer urlStringBuffer = new StringBuffer("http://10.0.0.172");
                urlStringBuffer.append(tempUrl.substring(contentBeginIdx));
                URL urltemp = new URL(urlStringBuffer.toString());
                httpConnection = (HttpURLConnection) urltemp.openConnection();
                httpConnection.setRequestProperty("X-Online-Host",
                        tempUrl.substring(offset, contentBeginIdx));
            } else {// wap 网络 访问https
                httpConnection = (HttpURLConnection) url.openConnection();
            }
        } else {
            String[] hostAndPort = NetworkUtil.getProxyHostAndPort(context);
            String host = hostAndPort[0];
            int port = Integer.parseInt(hostAndPort[1]);

            if (host != null && host.length() != 0 && port != -1) {// 电信wap
                // 普通移动net网络
                InetSocketAddress isa = new InetSocketAddress(host, port);
                java.net.Proxy proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, isa);
                httpConnection = (HttpURLConnection) url.openConnection(proxy);
            } else {
                // wifi 网络
                httpConnection = (HttpURLConnection) url.openConnection();
            }
        }

        httpConnection.setDoInput(true);
        httpConnection.setConnectTimeout(20000);
        httpConnection.setReadTimeout(20000);
        httpConnection.setRequestProperty("Accept", "*, */*");
        httpConnection.setRequestProperty("accept-charset", "utf-8");
        httpConnection.setRequestMethod("GET");
        return httpConnection;
    }
}
